#if !defined( MATRIX_H)
#define  MATRIX_H
#include <algorithm>
#include <istream>
#include <vector>
#include <assert.h>
#include <boost/shared_array.hpp>
using std::string;
template <typename T> class t_matrix_template
{
private:
	void copy_from_same_size(const t_matrix_template& other)
	{
		// copy from other to me
		for (unsigned j = 0; j < data.size(); ++j)
			std::copy(	other.data[j].get(), other.data[j].get() + data.size(),
						data[j].get());
	}
	std::vector<boost::shared_array<T> > data;
public:
    typedef T element_type;
	t_matrix_template(){}
	t_matrix_template(unsigned sz, T value)
	{
		allocate(sz, value);
	}
	t_matrix_template(const t_matrix_template& other)
	{
		allocate(other.data.size());
		copy(other);
	}
	void min(const t_matrix_template& other)
	{
		unsigned sz = std::min(size(), other.size());
		for (unsigned i = 0; i < sz; ++i)
			for (unsigned j = 0; j < sz; ++j)
				data[i][j] = std::min(data[i][j], other.data[i][j]);
	}
	
	void max(const t_matrix_template& other)
	{
		unsigned sz = std::min(size(), other.size());
		for (unsigned i = 0; i < sz; ++i)
			for (unsigned j = 0; j < sz; ++j)
				data[i][j] = std::max(data[i][j], other.data[i][j]);
	}
	
	void copy(const t_matrix_template& other)
	{
		partial_copy(other, other.size());
	}
	void partial_copy(const t_matrix_template& other, unsigned sz)
	{
		assert(sz <= other.size());
		assert(sz <= size());
		for (unsigned i = 0; i < sz; i++)
			std::copy (other.data[i].get(), other.data[i].get() + sz,
					data[i].get());
		
	}
	
	boost::shared_array<T>& operator[](unsigned i)  {return data[i];}
	const boost::shared_array<T>& operator[](unsigned i) const {return data[i];}
	unsigned size() const {return data.size();}
	void allocate(unsigned sz, T value)
	{
		allocate(sz);
		
		// fill with default value
		for (unsigned j = 0; j < sz; ++j)
			std::fill(data[j].get(), data[j].get() + sz, value);
	}
	void allocate(unsigned sz)
	{
		// reallocate me if necessary
		if (data.size() != sz)
		{
			data.clear();
			for (unsigned j = 0; j < sz; ++j)
				data.push_back(boost::shared_array<T>(new T [sz]));
		}
	}
	t_matrix_template& operator = (const t_matrix_template& other)
	{
		// reallocate me if necessary
		if (data.size() != other.data.size())
			allocate(other.data.size());
		
		copy(other);
		
		return *this;
	}

};
typedef t_matrix_template<double> t_matrix;
typedef t_matrix_template<bool> t_matrix_bool;

unsigned read_matrix_from_stream_in_fasta_pairs_format(	std::istream& infile,
														t_matrix& matrix,
														t_matrix_bool& missing_values,
														std::vector<std::string>& names,
														std::string& cluster_name,
														unsigned& missing_values,
														unsigned& duplicate_values,
														double default_value = 10000.0);

unsigned read_matrix_from_vector_of_pairs_format(		std::vector<std::string>::const_iterator begin,
														std::vector<std::string>::const_iterator end,
														t_matrix& matrix,
														t_matrix_bool& missing_values,
														std::vector<std::string>& names,
														unsigned& missing_values,
														unsigned& duplicate_values,
														double default_value = 10000.0);

#endif	//MATRIX_H

